
/*
 *  expression_evaluation.c
 *
 *
 *  Created by Yi Lin on 11-5-7.
 *  Copyright 2011 __MyCompanyName__. All rights reserved.
 *
 */

#include "runtime.h"
#include "backend.h"

struct type access_variable_type(char* variable, struct access_table *table);
struct variable access_variable(char* variable, struct access_table *table);

struct evaluation evaluate_identifier(char *identifier, struct access_table *table, int context);
struct evaluation evaluate_compare_expression
(enum expression_cate operator, struct expression *left, struct expression *right,
        struct access_table *table, int context);
struct evaluation evaluate_int(int value);
struct evaluation evaluate_boolean(int value);

struct evaluation evaluate_unary_numeric_expression
(enum expression_cate operator, struct expression *e,
        struct access_table *table, int context);

struct evaluation evaluate_binary_numeric_expression
(enum expression_cate operator, struct expression *left, struct expression *right,
        struct access_table *table, int context);

int evaluation_value(struct evaluation e, int context) {

    switch (e.cate) {
        case VALUE_BOOL:
        case VALUE_INT:
            return e.spec.value;

        case VARIABLE:
            return value_of(e.spec._variable);

        default:
            return 0;
    }

    return 0;

}

struct evaluation evaluate_expression
(struct expression *expression, struct access_table *table, int context) {

    printf("evaluate expression\n");
    switch (expression->cate) {
        case ASSIGN:

        case FUNCCALL:

        case COMPARE_EQ:
            return evaluate_compare_expression
                    (expression->cate,
                    expression->spec.compare_eq.first,
                    expression->spec.compare_eq.second,
                    table, context);

        case COMPARE_NE:
            return evaluate_compare_expression
                    (expression->cate,
                    expression->spec.compare_ne.first,
                    expression->spec.compare_ne.second,
                    table, context);

        case COMPARE_LT:
            return evaluate_compare_expression
                    (expression->cate,
                    expression->spec.compare_lt.first,
                    expression->spec.compare_lt.second,
                    table, context);

        case COMPARE_LE:
            return evaluate_compare_expression
                    (expression->cate,
                    expression->spec.compare_le.first,
                    expression->spec.compare_le.second,
                    table, context);

        case COMPARE_GT:
            return evaluate_compare_expression
                    (expression->cate,
                    expression->spec.compare_gt.first,
                    expression->spec.compare_gt.second,
                    table, context);

        case COMPARE_GE:
            return evaluate_compare_expression
                    (expression->cate,
                    expression->spec.compare_ge.first,
                    expression->spec.compare_ge.second,
                    table, context);

        case NUMERIC_ADD:
            return evaluate_binary_numeric_expression
                    (expression->cate,
                    expression->spec.numeric_add.first,
                    expression->spec.numeric_add.second,
                    table, context);

        case NUMERIC_SUB:
            return evaluate_binary_numeric_expression
                    (expression->cate,
                    expression->spec.numeric_sub.first,
                    expression->spec.numeric_sub.second,
                    table, context);

        case NUMERIC_MUL:
            return evaluate_binary_numeric_expression
                    (expression->cate,
                    expression->spec.numeric_mul.first,
                    expression->spec.numeric_mul.second,
                    table, context);

        case NUMERIC_DIV:
            return evaluate_binary_numeric_expression
                    (expression->cate,
                    expression->spec.numeric_div.first,
                    expression->spec.numeric_div.second,
                    table, context);

        case NUMERIC_MOD:
            return evaluate_binary_numeric_expression
                    (expression->cate,
                    expression->spec.numeric_mod.first,
                    expression->spec.numeric_mod.second,
                    table, context);

        case NUMERIC_LINC:
            return evaluate_unary_numeric_expression
                    (expression->cate,
                    expression->spec.numeric_linc.expression,
                    table, context);

        case NUMERIC_LDEC:
            return evaluate_unary_numeric_expression
                    (expression->cate,
                    expression->spec.numeric_ldec.expression,
                    table, context);

        case NUMERIC_LSUB:
            return evaluate_unary_numeric_expression
                    (expression->cate,
                    expression->spec.numeric_lsub.expression,
                    table, context);

        case NUMERIC_RINC:
            return evaluate_unary_numeric_expression
                    (expression->cate,
                    expression->spec.numeric_rinc.expression,
                    table, context);
            
        case NUMERIC_RDEC:
            return evaluate_unary_numeric_expression
                    (expression->cate,
                    expression->spec.numeric_rdec.expression,
                    table, context);

        case IDENTIFIER:
            return evaluate_identifier(expression->spec.identifier.identifier, table, context);

        case IMMEDIATE_INT:
            return evaluate_int(expression->spec.immediate_int.value);

        case IMMEDIATE_BOOL:
            return evaluate_boolean(expression->spec.immediate_bool.value);

        default:
            return INVALID_EVALUATION;
    }
}

struct evaluation evaluate_int(int value) {
    struct evaluation ret;
    ret.cate = VALUE_INT;
    ret.spec.value = value;
    return ret;
}

struct evaluation evaluate_boolean(int value) {
    struct evaluation ret;
    ret.cate = VALUE_BOOL;
    ret.spec.value = (value != 0);
    return ret;
}

struct evaluation evaluate_compare_expression
(enum expression_cate operator, struct expression *left, struct expression *right,
        struct access_table *table, int context) {

    struct evaluation ret;
    ret.cate = VALUE_BOOL;

    struct evaluation left_e = evaluate_expression(left, table, context);
    struct evaluation right_e = evaluate_expression(right, table, context);


    switch (operator) {
        case COMPARE_LT:
            ret.spec.value = (
                    evaluation_value(left_e, context) < evaluation_value(right_e, context));
            printf("evaluate comapre_lt (valid) %d < %d, %d \n",
                    evaluation_value(left_e, context),
                    evaluation_value(right_e, context),
                    ret.spec.value);

            return ret;

        default:
            return INVALID_EVALUATION;


    }
}

struct evaluation evaluate_identifier(char *identifier, struct access_table *table, int context) {

    struct evaluation ret_evaluation;
    ret_evaluation.cate = VARIABLE;
    ret_evaluation.spec._variable = access_variable(identifier, table);

    return ret_evaluation;
}

struct evaluation evaluate_unary_numeric_expression
(enum expression_cate operator, struct expression *e,
        struct access_table *table, int context){
    return INVALID_EVALUATION;
}


struct evaluation evaluate_binary_numeric_expression
(enum expression_cate operator, struct expression *left, struct expression *right,
        struct access_table *table, int context) {

    printf("evaluate binary numeric expression\n");
    struct evaluation left_e = evaluate_expression(left, table, context);
    struct evaluation right_e = evaluate_expression(right, table, context);

    int left_value = evaluation_value(left_e, context);
    int right_value = evaluation_value(right_e, context);

    struct evaluation ret_evaluation;
    ret_evaluation.cate = VALUE_INT;

    switch (operator) {
        case NUMERIC_ADD:
            printf("adding result %d\n", (left_value + right_value));
            ret_evaluation.spec.value = (left_value + right_value);
            break;

        default:
            return INVALID_EVALUATION;
    }

    return ret_evaluation;

}